package xnsq

import (
	"encoding/json"
	"runtime"
	"strings"
	"time"

	"gitee.com/xiaoyutab/xgotool/optional/xlog"
	"gitee.com/xiaoyutab/xgotool/xerror"
	"github.com/nsqio/go-nsq"
)

// 写入队列【若未开启队列的话，将不管队列方法执行的结果，即程序会运行0~1次，即使是失败，所以建议开启NSQ队列，用以保证任务能成功执行】
//
//	fun		写入的服务名称，对应读取时的名称配置
//	param	传入的参数，对应读取时的参数结构
func Set(funs string, param any) error {
	if funs == "" {
		return nil
	}
	lens := strings.Split(funs, ",")
	if len(lens) > 1 {
		for i := 0; i < len(lens); i++ {
			err := Set(lens[i], param)
			if err != nil {
				return err
			}
		}
		return nil
	}
	if _default.Set == "" {
		go not_nsq_run(funs, param)
		return nil
	}
	nsq_config := nsq.NewConfig()
	producer, err := nsq.NewProducer(_default.Set, nsq_config)
	if err != nil {
		return err
	}
	defer producer.Stop()
	send, _ := json.Marshal(NSQ{
		Func:  funs,
		Param: param,
		Time:  time.Now().Format(time.DateTime),
	})
	err = producer.Publish(_default.SetTopic, send)
	if err != nil {
		return err
	}
	return nil
}

// 写入延时队列
//
//	fun		写入的服务名称，对应读取时的名称配置
//	param	传入的参数，对应读取时的参数结构
//	t		延时时间【目前配置禁止超过1小时的延时任务，若某程序需要延迟1天的话请使用30min进行循环写入的形式来进行实现】
func SetDef(fun string, param any, t time.Duration) error {
	if fun == "" {
		return nil
	}
	lens := strings.Split(fun, ",")
	if len(lens) > 1 {
		for i := 0; i < len(lens); i++ {
			err := SetDef(lens[i], param, t)
			if err != nil {
				return err
			}
		}
		return nil
	}
	if _default.Set == "" {
		go func() {
			time.Sleep(t)
			not_nsq_run(fun, param)
		}()
		return nil
	}
	nsq_config := nsq.NewConfig()
	producer, err := nsq.NewProducer(_default.Set, nsq_config)
	if err != nil {
		return err
	}
	defer producer.Stop()
	nq := NSQ{
		Func:  fun,
		Param: param,
		Time:  time.Now().Format(time.DateTime),
	}
	if t.Hours() > 1 {
		nq.RunTime = time.Now().Add(t).Format(time.DateTime)
		t = time.Hour // 延时时间修改为1小时，避免出错
	}
	if t < 0 {
		return xerror.New("队列延时配置错误：" + t.String())
	}
	send, _ := json.Marshal(nq)
	err = producer.DeferredPublish(_default.SetTopic, t, send)
	if err != nil {
		return err
	}
	return nil
}

// 服务未开启的话则使用携程执行此程序
//
//	fun		写入的服务名称，对应读取时的名称配置
//	param	传入的参数，对应读取时的参数结构
func not_nsq_run(funs string, param any) {
	nsq_lock.RLock()
	defer nsq_lock.RUnlock()
	if tmps, ok := nsq_runtime[funs]; ok {
		fun := tmps.Func
		defer func() {
			if err := recover(); err != nil {
				// 捕获到panic错误
				_, file, line, _ := runtime.Caller(2)
				xlog.SaveAny('P', file, uint(line), "NSQ失败请求", xerror.New(funs+": "+err.(string)))
				// fmt.Println("NSQ失败请求[panic报错]：", file, line, router.Func, err)
			}
		}()
		// 存在路由的话
		param, _ := json.Marshal(param)
		err := fun(param)
		if err != nil {
			xlog.Error("NSQ失败请求", err)
		}
	}
}
